home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
pas2c.zip
/
TASK.C
< prev
next >
Wrap
Text File
|
1993-01-04
|
4KB
|
158 lines
/*
Task Switching (Coroutines)
Simple, non-preemptive tasking for Turbo C and Turbo Pascal 4.
Create tasks using the start_task routine, and switch tasks with
the switch_task routine. A few utility functions are provided to
allow enabling and disabling tasks, and to delay a task for a
specified time interval.
*/
#include <std.h>
#include <dos.h>
#include <process.h>
#include <setjmp.h>
#include <stdlib.h>
#include "task.h"
#define MAX_TASKS 16
LOCAL TASK FAR (*task_start)(VOID);
LOCAL jmp_buf tasks[MAX_TASKS];
LOCAL BOOL task_active[MAX_TASKS];
/* publish current_task variable if not compiling for Turbo Pascal. if
Turbo Pascal 4 is being used, a global integer called _current_task must
must be provided */
#ifndef PASCAL
COUNT current_task;
#endif
/* switch to next task - on 8Mhz 80286, takes 90 usecs to go from task
to task, 210 usec to skip 16 inactive tasks (without call overhead) */
VOID PASCAL FAR switch_task(VOID)
{
COUNT i;
/* save current task */
if (setjmp(tasks[current_task]) == 0)
{
/* select next active task */
for (i = (current_task + 1) % MAX_TASKS;
!task_active[i]; i = (i + 1) % MAX_TASKS)
;
/* start next task */
current_task = i;
longjmp(tasks[current_task], 1);
}
/* return to next task */
}
/* create and start a new task */
VOID PASCAL FAR start_task(COUNT id, VOID *stack, TASK FAR (*task)())
{
WORD stack_segment, stack_offset;
/* save current task context */
if (setjmp(tasks[current_task]) == 0)
{
/* task is now active, and current */
task_active[id] = YES;
current_task = id;
/* set new stack for task, copy task pointer to global data */
/* ensure new stack pointer is at top of stack, and even */
task_start = task;
stack_segment = FP_SEG(stack);
stack_offset = FP_OFF(stack);
/* WARNING!!! SPECIFIC TO BORLAND TURBO-C */
_SS = stack_segment;
_SP = stack_offset;
/* execute new task */
(*task_start)();
/* error, task returned - if using TPAS 4, will have to provide
an abort function in the Pascal section */
abort();
}
/* current task returns here */
}
/* enable task (allow it to be scheduled) */
VOID PASCAL FAR enable_task(COUNT id)
{
task_active[id] = YES;
}
/* disable task (remove from scheduling consideration) */
VOID PASCAL FAR disable_task(COUNT id)
{
task_active[id] = NO;
}
/* delay current task by milliseconds - this is only accurate to one timer
tick (1/18 second) -- multiple tasks can be "blocked" here, each has its
own copy of the timing variables, and respects its own timeout */
VOID PASCAL FAR delay_task(COUNT milliseconds)
{
LONG timeout;
LONG startsecs, endsecs, now;
BOOL crossover;
VOLATILE LONG *t = MK_FP(0x40,0x6C);
startsecs = *t;
/* compute ending time */
timeout = (LONG)milliseconds * 18L / 1000L;
if (timeout == 0L)
timeout = 1L;
endsecs = startsecs + timeout;
/* if ending time is past midnight, we have a time crossover
so adjust endsecs for the new day */
if (crossover = (endsecs >= 0x1800B0L))
endsecs = endsecs - 0x1800B0L;
FOREVER
{
/* allow other tasks */
switch_task();
/* get the current time */
now = *t;
/* see if delay is done */
if (crossover)
{
/* if midnight crossover, exit if time between start and end */
if (now < startsecs && now >= endsecs)
break;
}
else
/* no crossover, exit if time past end */
if (now >= endsecs)
break;
}
}
/* Initialize the tasker */
VOID PASCAL FAR initialize_task(VOID)
{
COUNT i;
/* all tasks inactive */
for (i = 0; i < MAX_TASKS; ++i)
task_active[i] = NO;
/* except for the main task */
task_active[0] = YES;
current_task = 0;
}